//
//  HMBlueToothManager.m
//  03-蓝牙开发
//
//  Created by HM09 on 2017/4/26.
//  Copyright © 2017年 itheima. All rights reserved.
//

#import "HMBlueToothManager.h"

@interface HMBlueToothManager ()<CBCentralManagerDelegate,CBPeripheralDelegate>{
    NSTimer *timer;
}

//蓝牙中心
@property(nonatomic,strong)CBCentralManager *centralManager;

//蓝牙外设
@property(nonatomic,strong)CBPeripheral *peripheral;

//扫描外设数组
@property(nonatomic,strong)NSMutableArray *scanArr;

@property (strong, nonatomic) CBCharacteristic* writeCharacteristic;


//扫描外设回调
@property(nonatomic,copy)void(^scanBlock)(NSArray *);
//连接外设回调
@property(nonatomic,copy)void(^connectBlock)(NSError *);
//断开外设回调
@property(nonatomic,copy)void(^disconnectBlock)(NSError *);
/**
 *  用于判断是否写入成功
 */
@property (nonatomic,assign) NSInteger writeA;
/**
 *  用于判断是否写入成功
 */
@property (nonatomic,assign) NSInteger writeB;


@end

@implementation HMBlueToothManager

+ (instancetype)shareInstance
{
    static HMBlueToothManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[HMBlueToothManager alloc] init];
    });
    
    return manager;
}

- (CBCentralManager *)centralManager
{
    if (_centralManager != nil) {
        return _centralManager;
    }
    
    //创建蓝牙中心
    _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
    
    return _centralManager;
}

//检测蓝牙是否可用
- (BOOL)availableBluethooth
{
    CBManagerState state = [self.centralManager state];
    /**
     CBManagerStateUnknown = 0,未知（第一次启动蓝牙中心是未知状态）
     CBManagerStateResetting,蓝牙不可用 （未知错误）
     CBManagerStateUnsupported,当前手机不支持蓝牙
     CBManagerStateUnauthorized,//未授权
     CBManagerStatePoweredOff,//蓝牙关闭
     CBManagerStatePoweredOn,//蓝牙可用
     */
    BOOL flag = NO;
    NSString *stateStr;
    switch (state) {
        case CBManagerStateUnknown:
            stateStr = @"第一次启动蓝牙";
            flag = YES;
            break;
        case CBManagerStateResetting:
            stateStr = @"蓝牙不可用";
            break;
        case CBManagerStateUnsupported:
            stateStr = @"蓝牙未授权";
            break;
        case CBManagerStatePoweredOff:
            stateStr = @"蓝牙关闭";
            //实际开发中可以给用户一个弹窗提示 点击跳转到系统蓝牙设置界面
            break;
        case CBManagerStatePoweredOn:
            flag = YES;
            stateStr = @"蓝牙可用";
            
            break;
        default:
            break;
    }
    return flag;
}



#pragma mark -流程1-扫描设备
- (void)beginScanCBPeripheral:(void (^)(NSArray *))updatePeripheral
{
    
    //1.检测当前手机蓝牙是否可用
    //使用断言  第一个参数是一个条件表达式  第二个参数：当条件表达式不成立时程序会崩溃，参数是崩溃的描述信息
//    NSAssert([self availableBluethooth] == YES, @"当前蓝牙不可用，请检查蓝牙状态");
    
    //2.开始扫描外设
    //参数表示扫描指定的外设 如果为nil则表示扫描所有的外设 opention：扫描配置 默认为nil
    [self.centralManager scanForPeripheralsWithServices:nil options:nil];
    
    //3.保存block
    self.scanBlock = updatePeripheral;
}


#pragma mark -流程3-连接外设
- (void)connectPeripheral:(CBPeripheral *)peripheral Completion:(void(^)(NSError *))completionBlock
{
    [self.centralManager connectPeripheral:peripheral options:nil];
    //保存连接回调
    self.connectBlock = completionBlock;
}

- (void)disconnectPeripheral:(CBPeripheral *)peripheral Completion:(void (^)(NSError *))discompletionBlock{
    [self.centralManager cancelPeripheralConnection:peripheral];
    self.disconnectBlock = discompletionBlock;
}


#pragma mark ---------蓝牙中心代理CBCentralManagerDelegate

//蓝牙状态更新
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    
}

//只要中心管理者初始化 就会触发此代理方法 判断手机蓝牙状态
#pragma mark -流程2-扫描到设备
//扫描到设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI
{
//    NSLog(@"唯一标识符：%@",peripheral.identifier);//设备唯一标识符
//    NSLog(@"外设:%@",peripheral);
//    NSLog(@"外设广告语：%@",advertisementData);
//    NSLog(@"当前外设信号：%@",RSSI);//信号主要由距离决定 信号越好 值越低
    
    
    //扫描到设备之后添加到扫描设备数组，用于界面显示
    //同一个设备会被重复扫描，添加数组时应该做一个重复判断
    if (self.scanArr == nil) {
        self.scanArr = [[NSMutableArray alloc] init];
    }
    
    //如果设备已经在数组中则不添加，否则添加
    if(![self.scanArr containsObject:peripheral])
    {
        [self.scanArr addObject:peripheral];
    }
    
    //送出回调，刷新UI
    if(self.scanBlock)
    {
        self.scanBlock([self.scanArr copy]);
    }
}

//连接外设成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    if (self.connectBlock) {
        self.connectBlock(nil);
    }
    
    //1.先保存外设
    self.peripheral = peripheral;
    //2.设置外设的代理
    self.peripheral.delegate = self;
    
#pragma mark -流程4 开发发现外设的服务
    //如果没有寻找服务的话，外设的服务数组是空
    //3.寻找外设的服务，为nil则表示寻找所有的服务
    [peripheral discoverServices:nil];
}


//外设连接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
//    NSLog(@"蓝牙连接失败：%@",error);
    if (self.connectBlock) {
        self.connectBlock(error);
    }
}

//外设断开：蓝牙的有效距离大概20M左右 由于中心与外设距离过远 或者外设本身问题导致断开
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
    if (self.disconnectBlock) {
        self.disconnectBlock(error);
    }
    self.bluetoothConnectionDisconnect(nil);
}

#pragma mark -----蓝牙外设代理CBPeripheralDelegate

//外设名字更新
- (void)peripheralDidUpdateName:(CBPeripheral *)peripheral
{
    
}

//外设信号变化
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
    
}

//读取到外设信号
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error
{
    
}

#pragma mark -流程5 发现外设的服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    //遍历外设的服务
    for (CBService *service in peripheral.services) {
        //实际工作中，智能硬件开发，硬件工程师会给你一份蓝牙协议，协议中会表名哪一个功能对应哪一个服务
//        NSLog(@"服务UUID：%@",service.UUID);
        #pragma mark -流程6 发现服务的特征
        //开始寻找服务的特征 第一个参数：特征UUID 为nil表示寻找所有特征 第二个参数：服务
        [peripheral discoverCharacteristics:nil forService:service];
    }


}

#pragma mark -流程6 发现服务的特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error
{
    
        for (CBService *service in peripheral.services) {
                //遍历服务的特征
                for (CBCharacteristic *characteristic in service.characteristics) {
                    
                    //特征是中心与外设进行数据交互最小的单位  所有的数据的发送都是发送给特征的  每一个特征都对应一个非常细节的功能：
//                    NSLog(@"特征UUID：%@",characteristic.UUID);
                    

                    //开启与特征之间的通知（中心与外设长连接，当特征发送数据过来时，能够及时收到）
                    [peripheral setNotifyValue:YES forCharacteristic:characteristic];
                    //读取特征服务，一次性
                    //       [peripheral readValueForCharacteristic:c];
                    
                    /*
                     设备读取UUID
                     */
                    if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FFE4"]]) {
                        self.writeCharacteristic = characteristic;
                        [self.peripheral setNotifyValue:YES forCharacteristic:self.writeCharacteristic];
                    }
                    
                    /**
                     设备写入UUID
                     */
                    if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FFE9"]]) {
                        self.writeCharacteristic = characteristic;
                        [self.peripheral setNotifyValue:YES forCharacteristic:self.writeCharacteristic];
                    
                }
               }
        }
   
}


//给特征发送数据回调
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
    
}



#pragma mark- 获取外设发来的数据，不论是read和notify,获取数据都是从这个方法中读取。
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error==nil) {
        NSMutableString *str1 = [NSMutableString stringWithFormat:@"%@",characteristic.value];
        //第一个参数是要删除的字符的索引，第二个是从此位开始要删除的位数
        [str1 deleteCharactersInRange:NSMakeRange(0, 1)];
        [str1 deleteCharactersInRange:NSMakeRange(str1.length-1, 1)];
        //删除字符串中的空格
        NSString *str4 = [str1 stringByReplacingOccurrencesOfString:@" " withString:@""];
        NSLog(@"写入数据为:%@",str4);
        if (self.dataBlock) {
            if ([str4 isEqualToString:@"68020001026d16"] || [str4 isEqualToString:@"68020007027316"] || [str4 isEqualToString:@"68020002026e16"] || [str4 isEqualToString:@"68020005027116"] ||[str4 isEqualToString:@"68020006027216"] || [str4 isEqualToString:@"68020009027516"] || [str4 isEqualToString:@"6808000a021107060c0200a816"] || [str4 rangeOfString:@"6836000b"].location == NSNotFound) {
                self.writeA = self.writeB;
                [timer invalidate];
                [self whetherWrittenToSuccess];
            }else{
                
            }
            
            self.dataBlock(characteristic.value);
        }
        //    NSLog(@"外设发送过来的数据:%@",characteristic.value.description );
    }else{
        
    }
    
}


#pragma mark- 中心读取外设实时数据
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        
    }
    NSMutableString *str1 = [NSMutableString stringWithFormat:@"%@",characteristic.value];
    //第一个参数是要删除的字符的索引，第二个是从此位开始要删除的位数
    [str1 deleteCharactersInRange:NSMakeRange(0, 1)];
    [str1 deleteCharactersInRange:NSMakeRange(str1.length-1, 1)];
    //删除字符串中的空格
    NSString *str4 = [str1 stringByReplacingOccurrencesOfString:@" " withString:@""];
    if (characteristic.isNotifying) {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FFE4"]]) {
            //读取外设数据
            [peripheral readValueForCharacteristic:characteristic];
            if (self.dataBlock) {
                if (![str4 isEqualToString:@"68020001026d16"]) {
                    
                }else{
                    self.writeA = self.writeB;
                    [timer invalidate];
                    [self whetherWrittenToSuccess];
                }
                self.dataBlock(characteristic.value);
            }
//            NSLog(@"%@",characteristic.value);
        }
        
    } else {
        
    }
}
/**
 发送命令
 */
- (void) sendData:(NSData *)data{
    
    /**
     通过CBPeripheral 类 将数据写入蓝牙外设中,蓝牙外设所识别的数据为十六进制数据,在ios系统代理方法中将十六进制数据改为 NSData 类型 ,但是该数据形式必须为十六进制数 0*ff 0*ff格式 在iToll中有将 字符串转化为 十六进制 再转化为 NSData的方法
     
     */
    [self.peripheral writeValue:data forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
    
}
// 需要注意的是特征的属性是否支持写数据

//分包发送蓝牙数据
-(void)sendMsgWithSubPackage:(NSData*)msgData
{
    self.writeA ++;
    timer = [NSTimer new];
    timer = [NSTimer scheduledTimerWithTimeInterval:4.0 target:self selector:@selector(whetherWrittenToSuccess) userInfo:nil repeats:NO];
    [LoaddingViewDJ show];
    for (int i = 0; i < [msgData length]; i += 20) {
        // 预加 最大包长度，如果依然小于总数据长度，可以取最大包数据大小
        if ((i + 20) < [msgData length]) {
            NSString *rangeStr = [NSString stringWithFormat:@"%i,%i", i, 20];
            NSData *subData = [msgData subdataWithRange:NSRangeFromString(rangeStr)];
            NSLog(@"++++++++>>>>%@",subData);
            [self.peripheral writeValue:subData forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
            //根据接收模块的处理能力做相应延时
            usleep(20 * 1000);
        }
        else {
            NSString *rangeStr = [NSString stringWithFormat:@"%i,%i", i, (int)([msgData length] - i)];
            NSData *subData = [msgData subdataWithRange:NSRangeFromString(rangeStr)];
            NSLog(@"++++++++>>>>%@",subData);
            [self.peripheral writeValue:subData forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
            usleep(20 * 1000);
        }
    }
}

- (void)whetherWrittenToSuccess{
    if (self.writeA == self.writeB) {
        [PopupView showMessageStr:@"写入成功"];
        NSLog(@"写入成功");
    }else{
        [PopupView showMessageStr:@"写入失败"];
        self.failureBlock(@"写入失败");
        NSLog(@"写入失败");
    }
    
    [LoaddingViewDJ dismiss];
    self.writeB = self.writeA;
}

@end
